import "./ripple.scss"
const ANIMATE_TRANSITION_IN = 140 // 结束动画时间
const ANIMATE_TRANSITION_OUT = 300 // 移除动画时间
const RIPPLE_ClASS = "v-ripple" // 外包 div 类名
const ANIMATION_CLASS = "v-ripple__animation" // 动画元素类名
const ANIMATION_CLASS_IN = "v-ripple__animation--in" // 动画元素进入时的类名
const ANIMATION_CLASS_OUT = "v-ripple__animation--out" // 动画元素离开时的类名
/**
 * 判断是否是手机 Touch 事件
 * @author Mengqun Li
 * @date 2021/02/08
 * @returns {boolean}  e is TouchEvent
 */
function isTouchEvent(Event) {
	return document.hasOwnProperty("ontouchstart")
}
/**
 * 为 el 添加 transform 属性
 * @date 2021/02/08
 * @param {HTMLElement} element
 * @param {string} value transform 属性
 */
function transform(element, value) {
	element.style.transform = value
	element.style.webkitTransform = value
}
/**
 * 计算动画元素应有的尺寸等属性值
 * @date 2021/02/08
 * @param {Event} event
 * @returns {object} {radius, scale, x, y, centerX, centerY}
 */
function calculate(element, event) {
	const offset = element.getBoundingClientRect()
	const target = isTouchEvent(event) ? event.touches[event.touches.length - 1] : event
	const radius = Math.sqrt(element.clientWidth ** 2 + element.clientHeight ** 2) / 2
	const localX = target.clientX - offset.left
	const localY = target.clientY - offset.top
	const centerX = `${(element.clientWidth - radius * 2) / 2}px`
	const centerY = `${(element.clientHeight - radius * 2) / 2}px`
	const x = `${localX - radius}px`
	const y = `${localY - radius}px`
	let scale = 0.3
	const computed = window.getComputedStyle(element)
	if (computed && +computed.borderRadius.replace("%", "") >= 50) {
		scale = 0.15
	}
	return { radius, scale, x, y, centerX, centerY }
}
/**
 * 获取动画的根元素
 * @date 2021/02/08
 * @returns {HTMLElement}
 */
function getRipple(element) {
	if (element.__ripple__) {
		return element.__ripple__
	}
	const ripple = document.createElement("div")
	ripple.classList.add(RIPPLE_ClASS)
	element.append(ripple)
	element.__ripple__ = ripple
	return ripple
}
/**
 * 动画入口
 * @date 2021/02/08
 * @param {Event} event
 * @returns {HTMLElement} 创建的执行动画元素
 */
function createAnimate(element, event) {
	const computed = window.getComputedStyle(element)
	// 如果根元素是 静态定位 则为它添加 相对定位 属性
	if (computed && computed.position === "static") {
		element.style.position = "relative"
		element.dataset.previousPosition = "static"
	}
	const ripple = getRipple(element)
	const animation = document.createElement("span")
	const { radius, scale, x, y, centerX, centerY } = calculate(element, event)
	const size = `${radius * 2}px`
	animation.classList.add(ANIMATION_CLASS)
	animation.style.width = size
	animation.style.height = size
	transform(animation, `translate(${x}, ${y}) scale3d(${scale},${scale},${scale})`)
	animation.activated = performance.now()
	ripple.append(animation)
	// 手动调配 将同步赋值转换为异步赋值
	// 使动画元素在下一个线程中获得赋值
	setTimeout(() => {
		animation.classList.add(ANIMATION_CLASS_IN)
		transform(animation, `translate(${centerX}, ${centerY}) scale3d(1,1,1)`)
	}, 0)
	return animation
}
const ripple = {
	show(event) {
		createAnimate(this, event)
	},
	hide(element) {
		if (!element.__ripple__) {
			return
		}
		const ripple = element.__ripple__
		const animation = ripple.children[ripple.children.length - 1]
		if (!animation) return
		if (animation.isHiding) return
		else animation.isHiding = true
		const diff = performance.now() - Number(animation.activated)
		const delay = Math.max(ANIMATE_TRANSITION_IN - diff, 0)
		setTimeout(() => {
			animation.classList.remove(ANIMATION_CLASS_IN)
			animation.classList.add(ANIMATION_CLASS_OUT)
			setTimeout(() => {
				ripple.removeChild(animation)
				// 如果原子队列中已经没有动画元素则将动画根元素移除
				if (ripple.children.length <= 0) {
					ripple.parentNode?.removeChild(ripple)
					delete element.__ripple__
				}
			}, ANIMATE_TRANSITION_OUT)
		}, delay)
	},
	mouseUp() {
		ripple.hide(this)
	},
}
function setChickHandle(element, binding) {
	if (binding.value === null || binding.value === undefined) binding.value = true
	if (binding.value == false) return
	element.addEventListener("mousedown", ripple.show.bind(element))
	window.addEventListener("mouseup", ripple.mouseUp.bind(element))
	element.addEventListener("touchstart", ripple.show.bind(element))
	window.addEventListener("touchend", ripple.mouseUp.bind(element))
}
function removeHandle(element) {
	delete element.__ripple__
	element.removeEventListener("mousedown", ripple.show.bind(element))
	window.removeEventListener("mouseup", ripple.mouseUp.bind(element))
	element.removeEventListener("touchstart", ripple.show.bind(element))
	window.removeEventListener("touchend", ripple.mouseUp.bind(element))
}
function updateHandle(element, binding) {
	if (binding.value) setChickHandle(element, binding)
	else removeHandle(element)
}
export default {
	mounted: setChickHandle,
	update: updateHandle,
	beforeUnmount: removeHandle,
}
